 
import { TowerHelper } from "utils/tower/TowerHelper";
import { APollingTask, CacheData } from "../../PollingStation";

export class CacheDataStructure<T extends ICacheStructure=ICacheStructure> extends CacheData<T>
{
    private structureTypeMap:Record<StructureConstant,Record<number,ICacheStructure<StructureConstant>>>={} as any;
    private structureTypeMap2:Record<StructureConstant,ICacheStructure<StructureConstant>[]>={} as any;
    private towerDamage:number[][]=[];
    private _rampart!:Record<number,boolean>;
    private costs_ !:CostMatrix;
    constructor(data:T[])
    {
        super(data);

    }

    Init(strcutres:Structure[])
    {
        const cachedata:T[]=[];
        this.structureTypeMap={}as any;
        this._rampart={};
        this.costs_ = new PathFinder.CostMatrix();
        // Memory.Debug[`${strcutres[0]?.pos.roomName} CacheDataStructure  length`]=strcutres.length;
        for(const item of strcutres)
        {

            const ret:T= {
                    id:item.id as string,
                    pos:item.pos,
                    structureType:item.structureType,
                    hits:item.hits,
                    hitsMax:item.hitsMax,
                    my:(item as any).my
                } as T;
                cachedata.push(ret);


            // if( item.structureType == STRUCTURE_ROAD)
            // {
            //     continue;
            // }
            const pos_key=ret.pos.x+ret.pos.y*50;
            if(this.structureTypeMap[item.structureType])
            {
                this.structureTypeMap[item.structureType][pos_key]=ret;
                this.structureTypeMap2[item.structureType].push(ret);
            }
            else
            {
                this.structureTypeMap[item.structureType]={[pos_key]:ret};
                this.structureTypeMap2[item.structureType]=[ret];
            }

            // 初始化rampart的位置
            if(item.structureType == STRUCTURE_RAMPART)
            {
                this._rampart[pos_key]=true;
            }

        }
        this.towerDamage = this.GetTowerDamage();
        this.cacheData = cachedata;
        this.setStructureCost();
    }
      get TowerDamage()
    {
        return  this.towerDamage ;
    }
    getStructure<T extends  StructureConstant=StructureConstant>(type:T,filter?:(e:ICacheStructure)=>boolean):ICacheStructure<T>[]|undefined
    {
        if(this.structureTypeMap2[type])
        {
            if(filter)
            {
                return this.structureTypeMap2[type].filter(filter)  as  ICacheStructure<T>[];
                
            }
            return   this.structureTypeMap2[type] as  ICacheStructure<T>[];
        }
        return undefined

    }
    
    lookStructure<T extends  StructureConstant=StructureConstant>(type:T,pos:IRoomPosition):ICacheStructure<T>|undefined
    {
        if(this.structureTypeMap[type])
        {
            return  this.structureTypeMap[type][pos.x+pos.y*50] as ICacheStructure<T>;
        }
        return undefined
    }
    public findInRange(pos:IRoomPosition,range:number)
    {
        const top = Math.max(0,pos.y-range);
        const left = Math.max(0,pos.x-range);
        const right = Math.min(49,pos.x+range);
        const bottom = Math.min(49,pos.y+range);

        const ret = Game.rooms[pos.roomName].lookForAtArea(LOOK_STRUCTURES,top,left,bottom,right,true);

        const result:Structure[]=[];
        for(const e of ret)
        {
            // if(this.isInRampart( e.structure.pos) || e.structure.structureType == STRUCTURE_WALL)
            // {
            //     continue;
            // }
            result.push(e.structure);
        }
        return result;
    }
    public getCostMatrix()
    {
        return this.costs_.clone();
    }
    public isInRampart(pos:RoomPosition):boolean
    {
        return this._rampart[pos.x+pos.y*50];
    }
    public inquireDamage(p:IFVector2)
    {
        if(this.TowerDamage[p.x])
        {
            
            return this.TowerDamage[p.x][p.y]*this.towerRadio();
        }
        return 0
    }
    private towerRadio()
    {
        const towers = this.getStructure(STRUCTURE_TOWER);
        if(towers && towers.length)
        {
            return towers.filter(e=>
                {
                    const tower = Game.getObjectById(e.id as Id<StructureTower>);
                    if(tower && tower.store.getUsedCapacity(RESOURCE_ENERGY)>9)
                    {
                        return true;
                    }
                    return false;
                }).length*1.0/towers.length
        }
        return 0
    }
    public inquireHeal(p:IFVector2)
    {
        return Math.floor(this.TowerDamage[p.x][p.y]*2/3)
    }
     protected GetTowerDamage():number[][]
    {
        if(!this.structureTypeMap[STRUCTURE_TOWER])
        {
            return [];
        }
        let Tower:ICacheStructure[] = Object.values(this.structureTypeMap[STRUCTURE_TOWER]);

        const cachetowers = Tower;

        const damagematrix:number[][]  =new Array<Array<number>>() ;
        for(let idx =0;idx<50;idx++)
        {
            damagematrix.push(new Array(50).fill(0));
        }

        let idx = 0;
        
        for(const cachetower of cachetowers)
        {
            const towerpos = cachetower.pos;
            for(let x=0;x<50;x++)
            {
                for(let y=0;y<50;y++)
                {
                    const damage =TowerHelper.TowerDamage( towerpos.distance2(x,y));
                    damagematrix[x][y]  = damagematrix[x][y] + damage;
                    this.costs_.set(x,y,damagematrix[x][y]<1500?0:damagematrix[x][y]/250);
                }
            }
        }
        return damagematrix;

    }
    private setStructureCost()
    {
        const costs_ =   this.costs_;
        this.cacheData.forEach(struct=>{
            if (struct.structureType !== STRUCTURE_CONTAINER &&
               (struct.structureType !== STRUCTURE_ROAD) &&(struct.structureType== STRUCTURE_RAMPART&& !struct.my)
           )
           {
            costs_.set(struct.pos.x, struct.pos.y, 0xff)
           }
       })
       return costs_;
    }
    // 先用ram的坐标计算哈希值保存。每次先比较哈希值。计算哈希值也需要遍历一遍。没啥意思
    // 哈希值不一致再逐个比较?
    public checkStructureDestory(structure:StructureConstant)
    {
        const structures = this.getStructure(structure);
        if(structures)
        {
            for(const e of  structures)
            {
                if(!Game.getObjectById(e.id))
                {
                    return true;
                }
            }
        }
        return false;
    }
}

export class CacheStructureTask<T = CacheDataStructure<ICacheStructure>> extends APollingTask<T>
{
    // roomName!:string;

    Update(...arg:any[]): void
    {
        // this.roomName = arg[0];
        const room = Game.rooms[this.code];
        if(room)
        {
            const structrures = room.find(FIND_STRUCTURES,{
                filter: item=> {
                   return item.structureType != STRUCTURE_CONTROLLER ;
                }});

            
            if(!this.value)
            {
                const cachestructure  =new CacheDataStructure<ICacheStructure>([]);
                this.value =cachestructure as unknown as T;
            }
            (this.value as any).Init(structrures);
           
            if(room.controller?.level ==8)
            {
                this.lastupdate = -1;
            }
            else 
            {
                this.lastupdate = Game.time + 1333;
            }
            
        }
    }

}
